查看原文
其他

肝了!Linux 开发调试经验

混说Linux 2022-11-19

毕业超过十年了,感慨岁月无情。做了若干年后台开发(之前做电信领域),大致说一下常见的开发心得和调试手段。使用互联网这么多年,收获的很多,总结的很少。本着互联网的精神,希望可以帮到互联网另一端的你。由于本人是做 C 语言的开发,陈述的经验也是 C 常用的调试手段。

调试这个蛋疼的事情,困扰着无数程序猿。很难有人保证自己写的代码一行错误都没有,有问题你就要查。怎么查?高手者,反汇编,看二进制;low 一点的就 gdb、看统计;再low就加打印。还可以再low 吗?可以,自己写bug,别人查。方法林林总总,长期掌握总可以找到适合自己的。


而调试的目的是什么?找到 BUG。想当年一个高手比喻的好:你找 BUG 其实你就是福尔摩斯,那为啥是福尔莫斯呢?想想你看到 BUG 案发现场,合格的程序都有日志、dump 内存、计数等基本案发现场吧。嗯,什么都没有,找写代码的人自己查。找问题就是在众多信息中,抽丝剥茧,找到疑点、反复推演程序运行的代码,最终找到作案的那一行或者几行代码。


这个过程是折磨人的地方,没有任何眉目时,令人茶不思饭不想。找到问题问题后,如打鸡血般兴奋,自己也会陶醉般飘飘然。真正受过折磨的人,才能体会到修改问题的滋味。



开发的程序大致要经过两个阶段,最终才可以上线发布。


功能开发阶段                

 

本阶段的主要目标是根据业务要求,开发程序。仅仅是 coder,仅仅是写 if else 吗?写程序真的是这样吗?如果是这样,那么 coder 会更加工厂化、枯燥化。


做事情要未雨绸缪,做程序更应该这样。大学 C 语言经典教材中定义程序为:程序 = 数据结构 + 算法。而实际生产的过程中,将商业程序做如下的补充定义,我觉得更合适:程序 = 数据结构 + 算法 + 业务逻辑(计算逻辑)+ 框架;


先说说为什么补充业务逻辑,有意义的程序本身就是某种业务逻辑(计算逻辑)的抽象。完成这个业务逻辑才是最终的目的,请不要拿一些算法研究的 code 和我抬杠。


其实作为开发人员,测试驱动开发(TDD)很好思考问题的思路。也许有人听过,也许有同学用过,如果感觉使用不好的兄弟,我可以告诉大家:应该是测试场景 + 场景驱动开发。对,仅仅是里面融入“场景”这个宾语,大家在做开发的时候,就有目的性和针对性。


任何一个业务逻辑,都可以拆分为多个业务场景。场景逐一解决,场景逐一测试,我们开发其实很简单。说的很简单,但是整个过程,需要 50%的时间思考解决问题场景, 20%的编码,30%的测试。其实思考问题的 50%的时间,可以放在任何时间去做(休息时,地铁上,班车上...),只要让自己足够的静,你就会将整个业务逻辑思考的很清楚,分解为多少个业务场景也很明确。对于复杂的业务场景,建议适当的做笔记,多从全局的业务逻辑考虑:自己细化的得到结论是否符合所有的业务场景。反复地修正,直到正确。


完成一个较大项目的若干需求,你可以一个需求一个需求的 code,也可以思考整个问题后再 code,往往后一种方式更容易得高分。


具体的编码时候,经过我们前面的深思熟虑,每个细节都已经很清楚了,采用迭代的方式,批量的交付小的功能点就可以了哈。


开发阶段的总结两个关键字:TDD + 迭代。需要详情的同学自行 baidu,google。


 

功能调试阶段                

 

调试的手段很多,读代码、打日志、gdb、统计、coredump 等,如果有精力也可以搞搞白盒测试什么的。测试的意图也很明显,确认代码是否按照正确的编码意图在运行!其实自己写的代码,自己还是可以轻松驾驭调试的,原因就是自己清楚代码的本意该如何运行,现在出现了什么问题。


程序猿的三大悲剧之一,就是不知道什么时候需要定位一个其他猿写的 bug。定位前也需要必须要理解另外一位程序猿写这段代码的意图是什么,否则没有办法定位。理解其他人写的代码的途径也就是通过阅读代码了解大致思路,通过日志、gdb、或者统计信息补充代码意图的更多细节,或者修正理解不对的思路。


这个过程可能很枯燥,也可能很有挑战,试图通过种种迹象去了解另外一个猿写代码的初衷和意图,会不会有窥探人家隐私的赶脚!


其实,上面说了这么多只是告诉大家调试好的前提,和调试的初衷。


一个优秀的猿,你会发现他有很多调试技巧,也就是很多调试手段获取自己想得到的信息。信息获取的多,自然就很容易清楚程序本身的意图。


调试工具的使用细节和说明,同学们可以自行baidu,google。


我在这里简单的阐述一下自己是怎么调试程序的,怎么理解各种工具的,欢迎大虾们指点交流。

1. 关于日志


如何打好日志绝对是门学问。日志打印多了,自然会影响后台程序的性能;同样打印的少了,没有办法定位问题;更苦逼的是打印到空指针,更有可能 coredump 掉自己的程序;


所以日志的技巧就是:少,却内容丰富。


如何少,其实就是汇聚。


能不能将表达同一个意思的打印减少?


能不能在关键异常的地方加上统计(输出统计)?


能不能不打?


能不能内存中记录关键信息,在想要的时候,控制其打印时机?


如何丰富,其实就是少打描述性词汇,多打有用的程序运行信息。


方法很多,大家多多思考。并且打印的优化,是反复优化的过程,不是一蹴而就的。曾经遇见一个大牛,测试部提问题了,这哥们从来不去定位。直接告诉测试的兄弟,帮忙执行以下软调,将收集的日志给他分析一下就可以解决问题。

2. 关于gdb


还有大牛说过:“我就是程序,程序就是我”。我常用 gdb 来检验自己对程序的理解。常用的 gdb 功能就是打印一些程序的运行信息,修改一些内部运行信息,构造复杂场景。


其实很简单,程序在什么场景下应该有什么样的行为,我自己的必须清楚。必须知道关键变量的信息是否正确,周期 gdb 出来,确认变量的信息是否正确,然后决定程序是否符合预期在执行。


可靠性的程序都有类似的保护机制,但是通常需要繁琐的构造测试条件,触发保护机制(如检测到丢包率很高,要告警等)。其实大多数的保护机制都是通过记录一些状态后,程序然后触发的保护机制。


其实,可以 gdb 构造出异常状态,确认告警机制是否生效。gdb 很好的补充这方面的测试和验证工作。

3. 关于统计


统计信息,是关键信息汇集的最好的例子。数据少,且信息明了。


电信软件中,很多模块都通过这样的信息:自证清白。也很容易发现问题出现哪里。


统计的实质,就是通过全局变量,记录一些程序正常,异常点的统计信息。然后通过某种手段输出出来。

4. 关于 coredump


大家看到 coredump 都会头痛,其实 coredump 也是很好的定位手段。


首先程序 coredump 后,会有详细的 coredump 文件,该文件详细地记录了程序在 core 之前的运行信息。gdb 这个 coredump 文件,你想看什么都可以。这只是简单的使用 coredump。


如果碰到复杂的问题,难搞的问题,其实也可以使用 coredump 来定位。


比如程序执行到一个十分不常见的代码分支,然后程序就 core 掉了,但是目前输出信息(日志等),根本没有办法进一步定位问题。


怎么办?有没有想过在复现问题的环节,出个调试版本的程序,在异常分支上主动触发内存异常,产生 coredump,利用 coredump 信息,来确定程序是如何异常的呢 ?

5. 关于代码修改


这个也是我常用的手段之一,反复地对比修改前后的代码,确认修改代码的准确性,全面性,反思自己代码修改是否全面?其实这里面工具就是 beyondcompare。

注:如需代码比较工具,公众号后台发送"beyondcompare"获取。


code 数载,偶有所得,记录于此,望猿有所得!


作者:beckyu






关注微信公众号『混说Linux』,后台点击 关于混说 即可添加作者微信。


往期推荐

惊了!CPU的工作原理,讲的太形象了!

IIC通信协议,搞懂这篇就够了

我被这道c语言笔试题整不会了

看完还不会指针,锤自己!


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存